#include "bpwm_nuvoton.h"
#include "NuMicro.h"

#define BPWM_ENABLE_MASK 0x05

struct BPWMStruct {
	uint32_t freTarget;
	double freReal;
	uint16_t duty0;
	uint16_t duty1;
};

struct BPWMManager {
	uint8_t bpwm0En : 1;
	uint8_t bpwm1En : 1;
	uint8_t reversed : 6;
	struct BPWMStruct bpwm0;
	struct BPWMStruct bpwm1;
};

static struct BPWMManager *sManager = NULL;

static void caculateFreReal(BPWM_AVAILABLE_LIST bpwm)
{
	uint32_t clk, prescale, period;
	
	if (bpwm == BPWM_PERIPHERAL_0) {
		if ((CLK->CLKSEL2 & CLK_CLKSEL2_BPWM0SEL_Msk) == 0) {
			clk = CLK_GetPLLClockFreq();
		}
		else {
			clk = CLK_GetPCLK0Freq();
		}
		prescale = BPWM0->CLKPSC + 1;
		period = BPWM0->PERIOD + 1;
		sManager->bpwm0.freReal = clk / (prescale * period);
	}
	else {
		if ((CLK->CLKSEL2 & CLK_CLKSEL2_BPWM1SEL_Msk) == 0) {
			clk = CLK_GetPLLClockFreq();
		}
		else {
			clk = CLK_GetPCLK1Freq();
		}
		prescale = BPWM1->CLKPSC + 1;
		period = BPWM1->PERIOD + 1;
		sManager->bpwm1.freReal = clk / (prescale * period);
	}
}

void BPWMInit(BPWM_AVAILABLE_LIST bpwm)
{
	static struct BPWMManager manager;
	
	manager.bpwm0En = 0;
	manager.bpwm1En = 0;
	manager.reversed = 0;
	manager.bpwm0.freTarget = 100000;
	manager.bpwm0.duty0 = 50;
	manager.bpwm0.duty1 = 50;
	manager.bpwm1.freTarget = 200000;
	manager.bpwm1.duty0 = 50;
	manager.bpwm1.duty1 = 50;
	sManager = &manager;
	
	SYS_UnlockReg();
	if (bpwm == BPWM_PERIPHERAL_0) {
		CLK_EnableModuleClock(BPWM0_MODULE);
		CLK_SetModuleClock(BPWM0_MODULE, CLK_CLKSEL2_BPWM0SEL_PLL, 0);
		SystemCoreClockUpdate();
		
		SYS->GPG_MFPH &= (SYS_GPG_MFPH_PG14MFP_Msk | SYS_GPG_MFPH_PG12MFP_Msk);
		SYS->GPG_MFPH |= (SYS_GPG_MFPH_PG14MFP_BPWM0_CH0 | SYS_GPG_MFPH_PG12MFP_BPWM0_CH2);
		GPIO_SetSlewCtl(PG, BIT12, GPIO_SLEWCTL_FAST);
		GPIO_SetSlewCtl(PG, BIT14, GPIO_SLEWCTL_FAST);
		
		BPWM_ConfigOutputChannel(BPWM0, 0, sManager->bpwm0.freTarget, sManager->bpwm0.duty0);
		BPWM_ConfigOutputChannel(BPWM0, 2, sManager->bpwm0.freTarget, sManager->bpwm0.duty1);
		
		BPWM_EnableOutput(BPWM0, BPWM_ENABLE_MASK);
	}
	else {
		CLK_EnableModuleClock(BPWM1_MODULE);
		CLK_SetModuleClock(BPWM1_MODULE, CLK_CLKSEL2_BPWM1SEL_PCLK1, 0);
		SystemCoreClockUpdate();
		
		SYS->GPC_MFPL &= SYS_GPC_MFPL_PC7MFP_Msk;
		SYS->GPC_MFPL |= SYS_GPC_MFPL_PC7MFP_BPWM1_CH0;
		SYS->GPA_MFPH &= SYS_GPA_MFPH_PA12MFP_Msk;
		SYS->GPA_MFPH |= SYS_GPA_MFPH_PA12MFP_BPWM1_CH2;
		GPIO_SetSlewCtl(PC, BIT7, GPIO_SLEWCTL_FAST);
		GPIO_SetSlewCtl(PA, BIT12, GPIO_SLEWCTL_FAST);
		
		BPWM_ConfigOutputChannel(BPWM1, 0, sManager->bpwm1.freTarget, sManager->bpwm1.duty0);
		BPWM_ConfigOutputChannel(BPWM1, 2, sManager->bpwm1.freTarget, sManager->bpwm1.duty1);
		
		BPWM_EnableOutput(BPWM1, BPWM_ENABLE_MASK);
	}
	SYS_LockReg();
	caculateFreReal(bpwm);
}

void BPWMSetEnable(BPWM_AVAILABLE_LIST bpwm, uint8_t newStatus)
{
	if (newStatus) {
		if (bpwm == BPWM_PERIPHERAL_0) {
			BPWM_Start(BPWM0, BPWM_ENABLE_MASK);
			sManager->bpwm0En = 1;
		}
		else {
			BPWM_Start(BPWM1, BPWM_ENABLE_MASK);
			sManager->bpwm1En = 1;
		}
	}
	else {
		if (bpwm == BPWM_PERIPHERAL_0) {
			BPWM_ForceStop(BPWM0, BPWM_ENABLE_MASK);
			sManager->bpwm0En = 0;
		}
		else {
			BPWM_ForceStop(BPWM1, BPWM_ENABLE_MASK);
			sManager->bpwm1En = 0;
		}
	}
}

void BPWMSetFre(BPWM_AVAILABLE_LIST bpwm, uint32_t fre)
{
	if (bpwm == BPWM_PERIPHERAL_0) {
		sManager->bpwm0.freTarget = fre;
		BPWM_ConfigOutputChannel(BPWM0, 0, sManager->bpwm0.freTarget, sManager->bpwm0.duty0);
		BPWM_ConfigOutputChannel(BPWM0, 2, sManager->bpwm0.freTarget, sManager->bpwm0.duty0);
	}
	else {
		sManager->bpwm1.freTarget = fre;
		BPWM_ConfigOutputChannel(BPWM1, 0, sManager->bpwm1.freTarget, sManager->bpwm1.duty0);
		BPWM_ConfigOutputChannel(BPWM1, 2, sManager->bpwm1.freTarget, sManager->bpwm1.duty0);
	}
	caculateFreReal(bpwm);
}

void BPWMSetDuty(BPWM_AVAILABLE_LIST bpwm, uint16_t duty, uint8_t ch)
{
	if (bpwm == BPWM_PERIPHERAL_0) {
		if (duty == 0 || duty > 100) {}
		else {
			if (ch == BPWM_CH0_MASK) {
				sManager->bpwm0.duty0 = duty;
				BPWM_ConfigOutputChannel(BPWM0, 0, sManager->bpwm0.freTarget, sManager->bpwm0.duty0);
			}
			else if (ch == BPWM_CH1_MASK) {
				sManager->bpwm0.duty1 = duty;
				BPWM_ConfigOutputChannel(BPWM0, 0, sManager->bpwm0.freTarget, sManager->bpwm0.duty1);
			}
		}
	}
	else {
		if (duty == 0 || duty > 100) {}
		else {
			if (ch == BPWM_CH0_MASK) {
				sManager->bpwm1.duty0 = duty;
				BPWM_ConfigOutputChannel(BPWM1, 0, sManager->bpwm1.freTarget, sManager->bpwm1.duty0);
			}
			else if (ch == BPWM_CH1_MASK) {
				sManager->bpwm0.duty1 = duty;
				BPWM_ConfigOutputChannel(BPWM1, 0, sManager->bpwm1.freTarget, sManager->bpwm1.duty1);
			}
		}
	}
}

uint32_t BPWMGetFre(BPWM_AVAILABLE_LIST bpwm)
{
	if (bpwm == BPWM_PERIPHERAL_0) {
		return sManager->bpwm0.freTarget;
	}
	else {
		return sManager->bpwm1.freTarget;
	}
}

double BPWMGetFreReal(BPWM_AVAILABLE_LIST bpwm)
{
	if (bpwm == BPWM_PERIPHERAL_0) {
		return sManager->bpwm0.freReal;
	}
	else {
		return sManager->bpwm1.freReal;
	}
}

uint16_t BPWMGetDuty(BPWM_AVAILABLE_LIST bpwm, uint16_t ch)
{
	if (bpwm == BPWM_PERIPHERAL_0) {
		if (ch == BPWM_CH0_MASK) {
			return sManager->bpwm0.duty0;
		}
		else if (ch == BPWM_CH1_MASK) {
			return sManager->bpwm0.duty1;
		}
		else {
			return 0;
		}
	}
	else {
		if (ch == BPWM_CH0_MASK) {
			return sManager->bpwm1.duty0;
		}
		else if (ch == BPWM_CH1_MASK) {
			return sManager->bpwm1.duty1;
		}
		else {
			return 0;
		}
	}
}

uint8_t BPWMGetOnOff(BPWM_AVAILABLE_LIST bpwm)
{
	if (bpwm == BPWM_PERIPHERAL_0) {
		return sManager->bpwm0En;
	}
	else {
		return sManager->bpwm1En;
	}
}
